/*
 * MIT License
 *
 * Copyright (c) 2019 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package cn.seaboot.commons.core;

import cn.seaboot.commons.lang.NameValueEntity;
import cn.seaboot.commons.lang.Warning;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 通用工具
 *
 * @author by Mr.css on 2018/5/22.
 */
public class CommonUtils {
    private CommonUtils() {
    }

    /**
     * SpringMVC转发Key
     */
    public static String redirect(String uri) {
        return "redirect:" + uri;
    }

    /**
     * SpringMVC重定向Key
     */
    public static String forward(String uri) {
        return "forward:" + uri;
    }

    /**
     * 是否是Ajax请求
     *
     * @param request -
     */
    public static boolean isAjax(ServletRequest request) {
        return "XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request).getHeader("X-Requested-With"));
    }

    /**
     * 获取请求域全部参数
     * <p>
     * 此函数仅适用于大部分场景，但并不是全部：
     * <p>
     * 请求允许出现同名参数，同一个 name，可以有很多个 value，value 本应该是一个数组，
     * 不过这种场景并不多见，如果声明成数组，会牺牲大量的算力，因此，将结果处理成 map。
     * <p>
     * 参考：{@link #getRequestEntities(HttpServletRequest)}
     *
     * @param request -
     */
    public static Map<String, Object> getRequestParams(HttpServletRequest request) {
        Map<String, Object> map = new HashMap<>();
        Enumeration<String> enumeration = request.getParameterNames();
        while (enumeration.hasMoreElements()) {
            String string = enumeration.nextElement();
            map.put(string, request.getParameter(string));
        }
        return map;
    }


    /**
     * 打印所有 setter 函数
     *
     * 日常编码过程中，要是有一大堆的值需要 set 的时候，这个函数能起到很好的辅助作用
     *
     * @param clazz -
     */
    public static void printSetter(Class<?> clazz) {
        Method[] fields = clazz.getDeclaredMethods();
        for (Method field: fields) {
            String name = field.getName();
            if(name.startsWith("set")){
                System.out.println("entity." + name + "()");
            }
        }
    }

    /**
     * 获取异常栈字符串
     *
     * @param t 异常
     * @return 信息
     */
    public static String getStackTraceString(Throwable t) {
        StringWriter sw = new StringWriter();
        t.printStackTrace(new PrintWriter(sw, true));
        return sw.getBuffer().toString();
    }


    /**
     * 获取请求域全部参数，每个参数可能对应多个值。
     *
     * @param request HTTP请求
     * @return 参数列表
     */
    public static List<NameValueEntity> getRequestEntities(HttpServletRequest request) {
        List<NameValueEntity> ret = new ArrayList<>();
        Enumeration<String> enumeration = request.getParameterNames();
        while (enumeration.hasMoreElements()) {
            String paramName = enumeration.nextElement();
            String[] values = request.getParameterValues(paramName);
            for (String value : values) {
                ret.add(new NameValueEntity(paramName, value));
            }
        }
        return ret;
    }

    /**
     * 是否为 null
     *
     * @param obj -
     * @return true/false
     */
    public static boolean isNull(Object obj) {
        return obj == null;
    }

    /**
     * 是否不为 null
     *
     * @param obj -
     * @return true/false
     */
    public static boolean isNotNull(Object obj) {
        return obj != null;
    }

    /**
     * 是否为空
     *
     * @param str -
     * @return true/false
     */
    public static boolean isEmpty(String str) {
        return str == null || str.isEmpty();
    }

    /**
     * 是否为空
     *
     * @param str -
     * @return true/false
     */
    public static boolean isNotEmpty(String str) {
        return (str != null && str.length() != 0);
    }

    /**
     * 是否为空
     *
     * @param l -
     * @return true/false
     */
    public static boolean isEmpty(@SuppressWarnings(Warning.RAW_TYPES) Collection l) {
        return l == null || l.isEmpty();
    }

    /**
     * 是否为空
     *
     * @param l -
     * @return true/false
     */
    public static boolean isNotEmpty(@SuppressWarnings(Warning.RAW_TYPES) Collection l) {
        return l != null && !l.isEmpty();
    }

    /**
     * 是否为空
     *
     * @param map -
     * @return true/false
     */
    public static boolean isEmpty(@SuppressWarnings(Warning.RAW_TYPES) Map map) {
        return map == null || map.isEmpty();
    }

    /**
     * 是否为空
     *
     * @param map -
     * @return true/false
     */
    public static boolean isNotEmpty(@SuppressWarnings(Warning.RAW_TYPES) Map map) {
        return map != null && !map.isEmpty();
    }

    /**
     * 是否为空
     *
     * @param pObj -
     * @return true/false
     */
    public static boolean isEmpty(Object pObj) {
        if (pObj == null) {
            return true;
        } else if (pObj instanceof String) {
            return ((String) pObj).trim().isEmpty();
        } else if ((pObj instanceof Collection)) {
            return ((Collection<?>) pObj).isEmpty();
        } else if ((pObj instanceof Map)) {
            return ((Map<?, ?>) pObj).isEmpty();
        } else if (pObj.getClass().isArray()) {
            return Array.getLength(pObj) == 0;
        } else {
            return false;
        }
    }

    /**
     * 是否不为空
     *
     * @param pObj -
     * @return true/false
     */
    public static boolean isNotEmpty(Object pObj) {
        return !isEmpty(pObj);
    }

    /**
     * 给出1个值，如果为空，则使用另一个
     *
     * @param src    原值
     * @param newObj 默认值
     * @param <T>    泛型
     * @return src/newObj
     */
    public static <T> T nvl(T src, T newObj) {
        return isEmpty(src) ? newObj : src;
    }
}
